﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Linq.Expressions;
using System.Data;

namespace PlanoAula.Repository
{
    public interface IGenericRepository<TEntity, TContext>
        where TEntity : class, new()
        where TContext : DbContext, new()
    {
        IQueryable<TEntity> GetAll();
        TEntity GetByKey(object key);
        void Delete(object entityKey);
        void Delete(TEntity entity);
        void Delete(Predicate<TEntity> predicate);
        void Delete(Func<TEntity, Boolean> predicate);

        bool HasPendingChanges { get; }
        TEntity Save(TEntity entity);
        void Add(TEntity entity);
        void Insert(TEntity entity);
        void Update(TEntity entity);
        void SaveChanges();
        EntityState State(TEntity entity);


    }
    public abstract class GenericRepository<TEntity, TContext> : IGenericRepository<TEntity, TContext>
        where TEntity : class, new()
        where TContext : DbContext, new()
    {

        public EntityState State(TEntity entity)
        {
            return Context.Entry(entity).State;
        }
        private DbSet<TEntity> _Dbset;
        private DbSet<TEntity> Dbset
        {
            get
            {
                if (_Dbset == null)
                    _Dbset = Context.Set<TEntity>();
                return _Dbset;
            }

        }

        private TContext _context;
        protected TContext Context
        {
            get
            {
                if (_context == null)
                    _context = new TContext();
                return _context;
            }
        }

        public virtual IQueryable<TEntity> GetAll()
        {
            return Dbset;
        }
        

        public virtual TEntity GetByKey(object key)
        {
            var entity = Dbset.Find(key);
            return entity;
        }

        public virtual void Delete(object entityKey)
        {
            var item = GetByKey(entityKey);
            this.Delete((TEntity)item);
        }
        public virtual void Delete(TEntity entity)
        {
            Dbset.Remove(entity);
            Context.SaveChanges();
        }
        public void Delete(Predicate<TEntity> predicate)
        {
            Dbset.RemoveAll(predicate);
            Context.SaveChanges();
        }
        public void Delete(Func<TEntity, Boolean> predicate)
        {
            IEnumerable<TEntity> objects = from o in Dbset.Where<TEntity>(predicate)
                                           select o;
            foreach (TEntity o in objects)
                Dbset.Remove(o);
            Context.SaveChanges();

        } // Delete
        public virtual bool HasPendingChanges
        {
            get
            {
                return Context.ChangeTracker.Entries().LongCount() > 0;
            }
        }
       

        /// <summary>
        /// Adiciona um item sem salvar no banco de dados
        /// </summary>
        /// <param name="entity"></param>
        public virtual void Add(TEntity entity)
        {
            Dbset.Add(entity);
        }
        public virtual void SaveChanges()
        {
            Context.SaveChanges();
        }
        public virtual TEntity Save(TEntity entity)
        {
            if (Context.Entry(entity).State == System.Data.EntityState.Detached)
            {
                Add(entity);
            }
            else
            {
                Update(entity);
            }
            return entity;
        }
        public virtual void Insert(TEntity entity)
        {
            lock (this)
            {
                bool pendente = this.HasPendingChanges;
                Dbset.Add(entity);
                if (!pendente)
                    Context.SaveChanges();
            }
        }

        public virtual void Update(TEntity entity)
        {
            //Adicionado para ver se vai funcionar
            //Context.Entry(entity).State = System.Data.EntityState.Modified;
            Context.SaveChanges();
        }
        #region outros metodos
        private Type orderByType;
        private Expression orderByExpression;
        private bool orderByDesc;

        private List<Type> thenByType;
        private List<Expression> thenByExpression;
        private List<bool> thenByDesc;

        public Expression<Func<TEntity, bool>> WherePredicate { get; set; }


        public void OrderBy<TKey>(Expression<Func<TEntity, TKey>> key)
        {
            orderByType = key.Body.Type;
            orderByExpression = key;
            orderByDesc = false;

            thenByType = new List<Type>();
            thenByExpression = new List<Expression>();
            thenByDesc = new List<bool>();
        }

        public void OrderByDesc<TKey>(Expression<Func<TEntity, TKey>> key)
        {
            orderByType = key.Body.Type;
            orderByExpression = key;
            orderByDesc = true;

            thenByType = new List<Type>();
            thenByExpression = new List<Expression>();
            thenByDesc = new List<bool>();
        }

        public void ThenBy<TKey>(Expression<Func<TEntity, TKey>> key)
        {
            thenByType.Add(key.Body.Type);
            thenByExpression.Add(key);
            thenByDesc.Add(false);
        }

        public void ThenByDesc<TKey>(Expression<Func<TEntity, TKey>> key)
        {
            thenByType.Add(key.Body.Type);
            thenByExpression.Add(key);
            thenByDesc.Add(true);
        }
        #endregion outrs metodos

    }
}
